Une liste des packages est disponible sur la page : http://bioconductor.org/packages/release/BiocViews.html#___Software
Cette liste reprend l'ensemble des paquets disponibles, avec leur nom, leurs maintainers ainsi qu'une courte description. Il est très simple d'extraire manuellement la liste des packages avec un simple copier-coller, et d'ensuite de parser le résultat. On peut également automatiser ce processus. Il y a cependant un détail important : la liste générée sur la page l'est via du Javascript, ce qui implique qu'on ne peut pas "simplement" ouvrir la page distante et parser son contenu.
Un rapide petit tour dans la source de la page permet de voir que les données sont en réalité extraites depuis un fichier packages.js
(http://bioconductor.org/packages/json/3.0/bioc/packages.js). Ce fichier contient une déclaration de variable Javascript data_annotation_packages
dont la valeur est un dictionnaire des packages disponibles. Vu que c'est essentiellement du JSON, on peut directement le parser avec Python :
In [21]:
import requests
import json
import BeautifulSoup as bs
URL = 'http://bioconductor.org/packages/json/3.0/bioc/packages.js'
PKG_URL = 'http://bioconductor.org/packages/release/bioc/html/{}.html'
OUTPUT_FILENAME = '../data/bioconductor_description.csv'
"""
URL = 'http://bioconductor.org/packages/json/3.0/data/annotation/packages.js'
PKG_URL = 'http://bioconductor.org/packages/release/data/annotation/html/{}.html'
OUTPUT_FILENAME = '../data/bioconductor_annotation_description.csv'
URL = 'http://bioconductor.org/packages/json/3.0/data/experiment/packages.js'
PKG_URL = 'http://bioconductor.org/packages/release/data/experiment/html/{}.html'
OUTPUT_FILENAME = '../data/bioconductor_experiment_description.csv'
"""
content = requests.get(URL).content
# Remove variable declaration
_, content = content[:-1].split(' = ', 1)
# JSON
content = json.loads(content)
La structure obtenue est assez simple : une clé "content" qui contient une liste des entrées. Chaque entrée est alors une liste de 3 éléments : un nom de package, une liste de maintainers et une courte description.
In [22]:
content['content'][:3]
Out[22]:
On peut donc aisément obtenir la liste des noms de packages présents sur Bioconductor :
In [23]:
packages = map(lambda x: x[0], content['content'])
In [24]:
packages[:10]
Out[24]:
Pour chaque package de nom NAME
, une page http://bioconductor.org/packages/release/bioc/html/NAME.html est disponible. Par exemple, pour le paquet a4
, la page http://bioconductor.org/packages/release/bioc/html/a4.html reprend une série d'informations concernant l'installation et l'usage du paquet. En particulier, cette page référence aussi, dans sa section Details, une série de couples clé/valeur correspondant aux entrées du fichier DESCRIPTION
que l'on retrouve dans les packages R.
Nous allons utiliser BeautifulSoup pour parser la page et récupérer cette information.
In [25]:
def description_for_package(package_name):
"""
Given a R package name on BioConductor, return a dictionary that contains
every key -> value that can be found in the "Details" section of the
related package page (PKG_URL).
"""
try:
content = requests.get(PKG_URL.format(package_name)).content
soup = bs.BeautifulSoup(content)
table = soup.find(name='table', attrs={'class': 'details'})
data = {}
for row in table.findChildren('tr'):
key, value = row.findChildren('td')
data[key.text] = value.text
return data
except Exception:
print 'Exception while working on', package_name
raise
Appliquons cette fonction sur tous les noms de packages repris dans la liste packages
de l'étape précédente, et récupérons les résultats. Nous plaçons ces résultats dans un dictionnaire, où la clé du dictionnaire est le nom du package, et la valeur de ce dictionnaire est l'information structurée récupérée depuis le site.
In [26]:
packages_data = {}
for package in packages:
data = description_for_package(package)
packages_data[package] = data
On peut maintenant facilement exporter ça vers un fichier .csv qui sera réutilisé plus tard.
In [27]:
import pandas
pandas.DataFrame.from_dict(packages_data, orient='index').to_csv(OUTPUT_FILENAME)